classdef ziData < handle
    %ZIDATA Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        data
        device
        
        nChannel
        nSample
        nNode
        
        sampleName
        nodeName
        
        default_component
        
        clockbase
    end
    
    methods
        function obj = ziData(data)
            %ZIDATA Construct an instance of this class
            %   Detailed explanation goes here
            obj.data = data;
            
            tags = fields(data);
            index = contains(tags, 'dev');
            if isempty(tags)
                error('Input data must have a field named ''devXXXX''');
            elseif length(find(index)) > 1
%                 warning('More than one devices detected. Only one is used.');
            end
            obj.device = tags{index};
            
            if any(contains(tags, 'clockbase'))
                obj.clockbase = data.clockbase;
            else
                try
                    obj.clockbase = ziDAQ('getDouble', ['/' obj.device '/clockbase']);
                catch
                    warning('clockbase not found, using MFLI default value %e.', 60e6);
                    obj.clockbase = 60e6;
                end
            end
        end
    end
    
    methods
%%
        function [chNames, nChannel] = getChannelNames(obj)
            chNames = fields(obj.data.(obj.device));
            nChannel = cellfun(@(ch) length(obj.data.(obj.device).(ch)), chNames);
        end
        function [sampleNames, nSample] = getSampleNames(obj, chName, idx)
            if nargin == 2
                idx = 1;
            end
            
            if any( contains(obj.getChannelNames, chName) )
                sampleNames =fields( obj.data.(obj.device).(chName)(idx) );
                nSample = cellfun(@(sa) length(obj.data.(obj.device).(chName)(idx).(sa)), sampleNames);
            else
                error('Channel %s not found', chName);
            end
        end

        function disp(obj)
            classnames = strsplit(class(obj), '.');
            if length(obj) > 1
                fprintf('%d %s object found.\n', length(obj), classnames{end} );
            end
            
            for k = 1:length(obj)
                obj_k = obj(k);
                [ch, nCh] = obj_k.getChannelNames;
                strHead = sprintf('|===============%s %d=================\n', classnames{end}, k);
                fprintf(strHead);
                for k = 1:length(ch)
                    fprintf(['|' obj_k.device '.%s(k), k=1...%d: \n'], ch{k}, nCh(k));
                    for idx = 1:nCh(k)
                        fprintf('|\t-----------------------\n');
                        [sa, nSa] = obj_k.getSampleNames(ch{k}, idx);
                        for q = 1:length(sa)
                            if nSa(q) > 1
                                nodes = strjoin(fields(obj_k.data.(obj_k.device).(ch{k})(idx).(sa{q}){1} ), ', ');
                                fprintf('|\t %s(%d).%s{q}.  q=%d...%d\n\t\t%s\n', ch{k}, idx, sa{q}, min(nSa(q), 1), nSa(q), nodes);
                            elseif nSa(q) == 1
                                sampleData = obj_k.data.(obj_k.device).(ch{k})(idx).(sa{q});
                                if iscell(sampleData)
                                    nodes = strjoin(fields( sampleData{1} ), ', ');
                                    fprintf('|\t %s(%d).%s{q}.  q=%d...%d\n|\t\t%s\n', ch{k}, idx, sa{q}, min(nSa(q), 1), nSa(q), nodes);
                                else
                                    nodes = strjoin(fields( sampleData ), ', ');
                                    fprintf('|\t %s(%d).%s.  \n|\t\t%s\n', ch{k}, idx, sa{q}, nodes);
                                end
                            else % empty sample
                                nodes = '[] - empty';
                                fprintf('|\t %s(%d).%s \n|\t\t%s\n', ch{k}, idx, sa{q}, nodes);
                            end
                        end
                    end
                end
                fprintf(['|' repmat('=', 1, length(strHead)-1) '\n\n']);
            end
        end
        
        function data = getData(obj, channelName, channelIndex, sampleName, sampleIndex, nodeName)
            channelIndexNum = toNumber(channelIndex);
            
            if contains(sampleName, '/')
                ss=strsplit(sampleName, '/');
                if length(ss) == 2
                    sampleData = obj.data.(obj.device).(channelName)(channelIndexNum).(ss{1}).(ss{2});
                else
                    error('Wrong sample name %s', sampleName);
                end
            else
                sampleData = obj.data.(obj.device).(channelName)(channelIndexNum).(sampleName);
            end
            
            if ischar(nodeName)
                if iscell(sampleData) 
                    data = sampleData{sampleIndex}.(nodeName);
                else
                    if isempty(sampleData)
                        data = [];
                    else
                        data = sampleData.(nodeName);  
                    end
                end
                % export coloum values
                if isrow(data) 
                    data = data.';
                end
            elseif iscell(nodeName)
                    dataCell = cellfun(@(node) obj.getData(channelName, channelIndexNum, sampleName, sampleIndex, node), nodeName, 'UniformOutput', false);
                    try 
                        data = cell2mat( dataCell ); 
                    catch
                        data = dataCell;
                        fprintf('data cell cannot be converted to mat.\n');
                    end
            end
        end
%% deprecated        
        function tags = listContent(obj, varargin)
            content = obj.get_content(varargin{:});
            tags = fields(content);
        end

        function [content, label] = get_content(obj, varargin)
            p=inputParser;
            p.addParameter('component', {obj.default_component});
            p.addParameter('node_index', 1);
            p.addParameter('sample_index', 1);
            p.parse(varargin{:});
            
            if ischar( p.Results.component )
                cmp = {p.Results.component};
            else
                cmp = p.Results.component;
            end
            
            try
                content = obj.data.(obj.device)...
                                  .(obj.nodeName)(p.Results.node_index)...
                                  .(obj.sampleName){p.Results.sample_index};
                label = cmp;
            catch
                error('Cannot get data content.');
            end

        end
        
        function col = get_grid_col_array(~, content)
            try
                col = content.header.gridcoldelta*double(0 : (content.header.gridcols-1))+content.header.gridcoloffset;
            catch
                error('cannot get header information.');
            end
        end
    end
end

function n = toNumber(channelIndex)
    if ischar(channelIndex)
        n = str2double(channelIndex) + 1;
    else
        n = channelIndex;
    end
end

